/*
 *	 Basically it works same as simulate function.
 *	 It generates machine code instead of simulating..
 */

#include "types.h"
#include "visa.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern instrp list_curp;

extern instrp list_headp;

extern char rmacro_content[MAX_MACRO][MAX_CODE_LENGTH];
int recognize_rmacro(char * str,int n);

/*
 *	reverses the bit ordering of a and writes it in ASCII into foutput.
 *	ex: a=97 gives 10000110
 */
void reverse_write_byte( int a, FILE * foutput){
	int i;
	for (i = 0; i < 8; i++){
		fputc('0'+a%2,foutput);
		a /= 2;
	}
	fputc('\n',foutput);
}
/*
 * generates machine instruction
 */
int write_code(int op, char * operand1, char * operand2, FILE * foutput){
	int val;
	int k;
	//now operands
	switch(op){
		case NOP:
		case AND:
		case ADD:
		case MULT:
		case CMP:
		case GOF:
		case NOT:
		case SLEEP:
			reverse_write_byte(op,foutput);
			break;
		case JMP:
		case JMZ:
		case JMN:
			reverse_write_byte(op,foutput);
			if ( operand1[0] != '@' ){
				printf("Line %d: Label names should start with @\n",list_curp->source_code_line_no);
				return -1;			
			}
			operand1++;
			if ( (k = recognize_rmacro(operand1,strlen(operand1))) == -1){
				printf("Line %d: You didn't define such a label\n",list_curp->source_code_line_no);
				return -1;
			}
			if ( sscanf(rmacro_content[k], "%d", &val) <= 0 ){
				printf("Line %d: This macro should be a number\n",list_curp->source_code_line_no);
				return -1;
			}
			reverse_write_byte(val,foutput);
			break;
		case LOAD:
			// Mikroya gore soldan 6. bit #/adr
			// rappel: 0 if #. 1 if adr
			if ( operand1[0] == '#') {
					//skip #
					operand1++;
			} else {
					op |= 32;
			}
			// Mikroya gore soldan 5. bit R0/R1
			if ( strcmp(operand2,"R1") == 0 ) op |= 16;
			else if ( strcmp(operand2, "R0") ){
				printf("Line %d: You forgot to write a number\n",list_curp->source_code_line_no);
				return -1;
			}
			reverse_write_byte(op,foutput);
			if ( !sscanf(operand1, "%d", &val) ){
					if ( (k = recognize_rmacro(operand1,strlen(operand1))) == -1 ){
						printf("Line %d: I'm not a number! I'm a free man!\n",list_curp->source_code_line_no);
						return -1;
					}
					//then it is a macro
					if( !sscanf(rmacro_content[k],"%d",&val) ){
						printf("Line %d: Left argument should be a number\n",list_curp->source_code_line_no);
						return -1;
					}
			}
			reverse_write_byte(val,foutput);
			break;
		case STORE:
			// Mikroya gore soldan 5. bit R0/R1
			if ( strcmp(operand1,"R1") == 0 ) op |= 16;
			else if ( strcmp(operand1, "R0") ){
				printf("Line %d: I'm not a number! I'm a free man!\n",list_curp->source_code_line_no);
				return -1;
			}
			reverse_write_byte(op,foutput);
			if ( !sscanf(operand2, "%d", &val) ){
					k = recognize_rmacro(operand2,strlen(operand2));
					//printf(":<%s> of %d\n", operand2,k);
					if ( k == -1 ){
						printf("Line %d: I'm not a number! I'm a free man!\n",list_curp->source_code_line_no);
						return -1;
					}
					//then it is a macro
					if( !sscanf(rmacro_content[k],"%d",&val) ){
						printf("Line %d: Left argument should be a number\n",list_curp->source_code_line_no);
						return -1;
					}
			}
			reverse_write_byte(val,foutput);
			break;
		default:;
	}
	return 0;
}


/*
 *
 */
int compile(FILE * foutput){
	int i,j;

	//current instruction
	char * code;
	int op;
	char operand1[MAX_CODE_LENGTH];
	char operand2[MAX_CODE_LENGTH];
	
	list_curp = list_headp;


	while (list_curp){

		code = list_curp->code;

		//Get first token. if the line is empty skip it.
		if ( !get_token(code,0,&i,&j) ){
			list_curp = list_curp->next;
			continue;
		}

		switch( op = recognize_opcode(code+i, j-i+1) ){
		case LOAD:
		case STORE:
			//get first arg
			if ( !get_token(code,j+1,&i,&j)){
				printf("Line %d: This command expects 2 operands\n", list_curp->line_no);
				return -1;
			}
			strncpy(operand1,code+i, j-i+1);
			operand1[j-i+1] = 0;

			//get second arg
			if ( !get_token(code,j+1,&i,&j)){
				printf("Line %d: This command expects 2 operands\n", list_curp->line_no);
				return -1;
			}
			strncpy(operand2,code+i, j-i+1);
			operand2[j-i+1] = 0;

			//do it
			if ( write_code(op, operand1, operand2, foutput) )return -1;
			break;
		case NOT:
		case JMN:
		case JMZ:
		case JMP:
			//get arg
			if ( !get_token(code,j+1,&i,&j)){
				printf("Line %d: This command expects 2 operands\n", list_curp->line_no);
				return -1;
			}
			strncpy(operand1,code+i, j-i+1);
			operand1[j-i+1] = 0;

			//do it
			//do_opcode1(op,operand1);
			if ( write_code(op, operand1, NULL, foutput) ) return -1;
			break;
		case NOP:
		case AND:
		case ADD:
		case MULT:
		case CMP:
		case GOF:
		case SLEEP:
			if (write_code(op,NULL,NULL,foutput) ) return -1;
			break;
		default:;
			//empty line or comment
		}
		list_curp = list_curp->next;
	}
	return 0;

}
